home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1996
/
MacHack 1996.toast
/
Presentations
/
Presentations ’93
/
Macintosh as Internet Server ƒ
/
inetd
/
TCP.cp
< prev
next >
Wrap
Text File
|
1993-04-27
|
6KB
|
279 lines
#include "TCP.h"
#include "InetD.h"
#include <UFailure.h>
#include <Resources.h>
#include <Devices.h>
#include <Processes.h>
TCPListener::TCPListener(InetD* daemon)
{
fDaemon = daemon;
fBufferSize = 0;
}
TCPListener::~TCPListener()
{
this->Release();
}
/* I'm assigning the result of a memory allocation directly to a
field, but its okay. The class isn't declared as a HandleObject
because I need to get at it during interrupt.
*/
Boolean
TCPListener::Initialize()
{
int numPBs;
int i;
Handle resHandle;
short theID;
ResType theType;
Str255 theName;
resHandle = GetResource('BUFF', 128);
FailResError();
fBufferSize = *((int*) *resHandle);
numPBs = CountResources('TCP ');
if (numPBs > 0) {
if (!fQueue.AllocPBs(numPBs, sizeof(MyTCPiopb)))
FailNIL((void*) 0);
for (i = 1; i <= numPBs; i++) {
resHandle = GetIndResource('TCP ', i);
FailResError();
GetResInfo(resHandle, &theID, &theType, theName);
this->ListenOn((tcp_port) theID);
ReleaseResource(resHandle);
}
return true;
}
else
return false;
}
void
TCPListener::DoNull()
{
MyTCPiopb* pb;
short port;
Handle resHandle;
FailInfo fi;
PSN dummy;
while ((pb = (MyTCPiopb*) fQueue.GetCompletedPB()) != nil) {
if ((pb->tcppb.csCode == TCPPassiveOpen) && (pb->tcppb.ioResult == noErr)) {
port = pb->tcppb.csParam.open.localPort;
resHandle = GetResource('TCP ', port);
FailResError();
HLock(resHandle);
fDaemon->LogIt(false, "Attempting to launch %P\n", ((FSSpec*) *resHandle)->name);
if (fi.Try()) {
fDaemon->Launch(pb->tcppb.tcpStream, (FSSpec*) *resHandle, 'TSTR', &dummy);
fi.Success();
}
else {
this->CloseDown(pb);
fDaemon->LogIt(true, "InetD:\n\nFailed to launch %P for TCP port %d\n",
((FSSpec*) *resHandle)->name, port);
}
fQueue.RecyclePB((ParmBlkPtr) pb);
this->ListenOn(port);
HUnlock(resHandle);
ReleaseResource(resHandle);
}
else {
fQueue.RecyclePB((ParmBlkPtr) pb);
}
}
}
void
TCPListener::ListenOn(tcp_port thePort)
{
MyTCPiopb* pb = nil;
Ptr rcvPtr = nil;
rcvPtr = NewPtrSys(fBufferSize);
FailMemError();
pb = (MyTCPiopb*) fQueue.GetUnusedPB();
FailNIL(pb);
pb->myA5 = SetCurrentA5();
pb->listener = this;
pb->tcppb.csCode = TCPCreate;
pb->tcppb.ioCRefNum = fDaemon->GetDriver();
pb->tcppb.csParam.create.rcvBuff = rcvPtr;
pb->tcppb.csParam.create.rcvBuffLen = fBufferSize;
pb->tcppb.csParam.create.notifyProc = &TCPNotify;
pb->tcppb.csParam.create.userDataPtr = (Ptr) this;
FailOSErr(PBControlSync((ParmBlkPtr) pb));
pb->tcppb.csCode = TCPPassiveOpen;
pb->tcppb.ioCRefNum = fDaemon->GetDriver();
pb->tcppb.ioCompletion = &TCPCompletion;
pb->tcppb.csParam.open.ulpTimeoutValue = 0;
pb->tcppb.csParam.open.ulpTimeoutAction = 1;
pb->tcppb.csParam.open.validityFlags = 0xC0;
pb->tcppb.csParam.open.commandTimeoutValue = 0;
pb->tcppb.csParam.open.remoteHost = 0;
pb->tcppb.csParam.open.remotePort = 0;
pb->tcppb.csParam.open.localHost = 0;
pb->tcppb.csParam.open.localPort = thePort;
pb->tcppb.csParam.open.tosFlags = 0;
pb->tcppb.csParam.open.precedence = 0;
pb->tcppb.csParam.open.dontFrag = 0;
pb->tcppb.csParam.open.timeToLive = 0;
pb->tcppb.csParam.open.security = 0;
pb->tcppb.csParam.open.optionCnt = 0;
FailOSErr(PBControlAsync((ParmBlkPtr) pb));
}
void
TCPListener::CloseDown(MyTCPiopb* pb)
{
pb->tcppb.csCode = TCPClose;
pb->tcppb.ioCRefNum = fDaemon->GetDriver();
pb->tcppb.csParam.close.ulpTimeoutValue = 3;
pb->tcppb.csParam.close.ulpTimeoutAction = 0;
pb->tcppb.csParam.close.validityFlags = 0xC0;
pb->tcppb.csParam.close.userDataPtr = nil;
FailOSErr(PBControlSync((ParmBlkPtr) pb));
pb->tcppb.csCode = TCPRelease;
pb->tcppb.csParam.create.userDataPtr = nil;
FailOSErr(PBControlSync((ParmBlkPtr) pb));
DisposePtr(pb->tcppb.csParam.create.rcvBuff);
FailMemError();
}
/* This actually does the Right Thing. The CQueue class is designed
specifically for handling system parameter blocks and keeps track
of them through various states of progress. The CleanQs function
only return PBs that are in use, in progress, or completed but
awaiting processing - not unused PBs.
*/
void
TCPListener::Release()
{
MyTCPiopb* pb = nil;
while ((pb = (MyTCPiopb*) fQueue.CleanQs()) != nil)
this->CloseDown(pb);
}
void
TCPListener::NotifyMe(PSNPtr psn, StreamPtr stream, ProcPtr proc, Ptr usr)
{
NoteeItem* nu = new NoteeItem;
FailNIL(nu);
nu->fPSN.highLongOfPSN = psn->highLongOfPSN;
nu->fPSN.lowLongOfPSN = psn->lowLongOfPSN;
nu->fStream = stream;
nu->fProc = proc;
nu->fUsrPtr = usr;
fNoteeList.PutOn(nu);
}
/* This currently gets called with the PSN of every app that
we launch, even the ones that don't request ASR service,
hence its okay if the notee isn't found.
*/
void
TCPListener::UnNotify(PSNPtr psn)
{
NoteeItem* old = fNoteeList.GetItem(psn);
if (old) {
fNoteeList.TakeOff(old);
delete old;
}
}
NoteeItem::NoteeItem()
{
fPSN.highLongOfPSN = 0;
fPSN.lowLongOfPSN = 0;
fStream = nil;
fProc = nil;
fUsrPtr = nil;
}
/* Its okay if these don't find the item, see the comment
on the UnNotify function above.
*/
NoteeItem*
NoteeList::GetItem(PSNPtr psn)
{
NoteeItem* it = (NoteeItem*) this->First();
while (it && (!SameProcesses(psn, &it->fPSN)))
it = (NoteeItem*) it->fNext;
// if (it == nil) DebugStr("\pdidn't find it…");
return it;
}
NoteeItem*
NoteeList::GetItem(StreamPtr stream)
{
NoteeItem* it = (NoteeItem*) this->First();
while (it && (stream != it->fStream))
it = (NoteeItem*) it->fNext;
// if (it == nil) DebugStr("\pdidn't find it…");
return it;
}
void
TCPCompletion(TCPiopb* in)
{
MyTCPiopb* pb = (MyTCPiopb*) in;
long thisA5 = SetA5(pb->myA5);
if (pb->tcppb.ioResult == noErr) {
pb->listener->fQueue.StoreCompletedPB((ParmBlkPtr) pb);
WakeUpProcess(&pb->listener->fDaemon->fPSN);
}
SetA5(thisA5);
}
pascal void
TCPNotify( StreamPtr stream,
unsigned short eventCode,
Ptr usr,
unsigned short termReason,
struct ICMPReport* icmp)
{
NoteeItem* them;
them = ((TCPListener*) usr)->fNoteeList.GetItem(stream);
if (them && them->fProc)
(*(TCPNotifyProc) them->fProc)(stream, eventCode, them->fUsrPtr, termReason, icmp);
}